// Copyright (c) Lawrence Livermore National Security, LLC and other VisIt
// Project developers.  See the top-level LICENSE file for dates and other
// details.  No copyright assignment is required to contribute to VisIt.

#include <MRUCache.h>
#include <visitstream.h>
#include <string>
#include <stdio.h>

#include <cstring>
#include <cstdlib>

#define CACHE_SIZE 20

using std::string;
using std::vector;

// define some types via a typedef
typedef MRUCache<string, char*, MRUCache_Free, CACHE_SIZE> FreeCache;
typedef MRUCache<string, int*, MRUCache_Delete, CACHE_SIZE> DelCache;

typedef struct {
   int a;
   float b;
   char c;
   double d;
} foo_t;

void DeleteAFoo(void *p)
{
    foo_t *fp = (foo_t*) p;
    free(fp);
}

int main()
{
    int i;

    // make caches for each of the kinds of delete
    MRUCache<string, int, MRUCache_DontDelete, CACHE_SIZE> dontCache;
    FreeCache freeCache;
    MRUCache<string, foo_t*, MRUCache_CallbackDelete, CACHE_SIZE> callbackCache(DeleteAFoo);
    DelCache delCache;
    MRUCache<string, float*, MRUCache_ArrayDelete, CACHE_SIZE> adelCache;

    cout << "Cache size is set to " << CACHE_SIZE << " items." << endl;

    // load caches and see how things behave
    for (i = 0; i < 2*CACHE_SIZE; i++)
    {
        cout << "Iteration " << i << endl;
        char tmp[256];
        sprintf(tmp, "item %02d", i);
        string tmpStr = string(tmp);

        // create entries in each kind of cache
        dontCache[tmpStr] = i;

        freeCache[tmpStr] = strdup(tmp);

        foo_t *fp = (foo_t*) malloc(sizeof(foo_t));
        fp->a = i;
        fp->b = (float) i / 2*CACHE_SIZE;
        fp->c = '\0';
        fp->d = (double) i / 2*CACHE_SIZE;
        callbackCache[tmpStr] = fp;

        int *ip = new int;
        *ip = i;
        delCache[tmpStr] = ip;

        float *fltp = new float[10];
        for (int j = 0; j < 10; j++)
            fltp[j] = (float) j;
        adelCache[tmpStr] = fltp;
    }

    DelCache::iterator it;
    const string fullName = "item 2";
    const int n = fullName.size();
    vector<string> keysToRemove;

    cout << "Iterating to find and remove all keys beginning with \"" << fullName.c_str() << "\"" << endl; 
    for (it = delCache.begin(); it != delCache.end(); it++)
    {
        if (fullName.compare(0, n, it->first, 0, n) == 0)
            keysToRemove.push_back(it->first);
    }
    for (size_t j = 0; j < keysToRemove.size(); j++)
    {
        dontCache.remove(keysToRemove[j]);
        freeCache.remove(keysToRemove[j]);
        callbackCache.remove(keysToRemove[j]);
        delCache.remove(keysToRemove[j]);
        adelCache.remove(keysToRemove[j]);
        cout << "Removed item with key \"" << keysToRemove[j].c_str() << "\"" << endl;
    }
    cout << "Iterating to see what remains in cache" << endl;
    for (it = delCache.begin(); it != delCache.end(); it++)
        cout << "Item with key \"" << it->first.c_str() << "\" still in cache" << endl;

    //
    // Test clear and adjustments in cache size
    //
    cout << "Clearing some caches" << endl;
    dontCache.clear();
    delCache.clear();
    if (dontCache.size() != 0 || delCache.size() != 0)
        cerr << "Problem with clearing caches" << endl;

    //
    // Set cache size downwards
    //
    cout << "Resizing some caches to most recently used 5 items." << endl;
    freeCache.numslots(5);
    adelCache.numslots(5);
    callbackCache.numslots(5);
    FreeCache::iterator fit;
    for (fit = freeCache.begin(); fit != freeCache.end(); fit++)
        cout << "Item with key \"" << fit->first.c_str() << "\" still in Free cache" << endl;
    MRUCache<string, float*, MRUCache_ArrayDelete, CACHE_SIZE>::iterator adit;
    for (adit = adelCache.begin(); adit != adelCache.end(); adit++)
        cout << "Item with key \"" << adit->first.c_str() << "\" still in delete [] cache" << endl;
    MRUCache<string, foo_t*, MRUCache_CallbackDelete, CACHE_SIZE>::iterator cbit;
    for (cbit = callbackCache.begin(); cbit != callbackCache.end(); cbit++)
        cout << "Item with key \"" << cbit->first.c_str() << "\" still in callbackCache cache" << endl;
}
